package org.firebug;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: 张晓东
 * @Date: 2020/4/22 14:50
 */
public class Client {
    private static final Map<Class, Map<Class, BeanCopier>> COPIERS = new ConcurrentHashMap<>();

    private static <S, T> boolean contains(Class<S> source, Class<T> target) {
        return COPIERS.containsKey(source) && COPIERS.get(source).containsKey(target);
    }

    private static <S, T> void register(Class<S> source, Class<T> target) {
        if (!COPIERS.containsKey(source)) {
            COPIERS.put(source, new ConcurrentHashMap<>());
        }
        Map<Class, BeanCopier> copier2 = COPIERS.get(source);
        if (!copier2.containsKey(target)) {
            copier2.put(target, BeanCopier.create(source, target, false));
        }
    }

    public static <S, T> BeanCopier getCopier(Class<S> source, Class<T> target) {
        if (!contains(source, target)) {
            register(source, target);
        }
        return COPIERS.get(source).get(target);
    }

    /**
     * source(实体) -> target(实体)
     */
    public static <S, T> void copyProperties(S source, T target) {
        getCopier(source.getClass(), target.getClass()).copy(source, target, null);
    }

    /**
     * List<S>  -> List<T>(需要先填充空对象)
     */
    public static <S, T> void copyProperties(List<S> sources, Class<S> sourceClass, List<T> targets, Class<T> targetClass) {
        BeanCopier copier = getCopier(sourceClass, targetClass);
        copyProperties(sources, targets, copier);
    }

    /**
     * List<S>  -> List<T>(需要先填充空对象)
     */
    public static <S, T> void copyProperties(List<S> sources, List<T> targets, BeanCopier copier) {
        Assert.isTrue(sources.size() == targets.size(), "需要sources.size() == targets.size()");
        for (int i = 0; i < sources.size(); i++) {
            copier.copy(sources.get(i), targets.get(i), null);
        }
    }

    /**
     * Collection<Bean> -> List<field>
     * 例：Collection<Bean> -> List<id>: collectField(users, t -> t.getId());
     */
    public static <B, F> List<F> collectField(Collection<B> beans, Function<B, F> field) {
        return beans.stream().map(b -> field.apply(b)).collect(Collectors.toList());
    }

    /**
     * Collection<Bean> -> Map<field,Bean>
     * 例：Map<id, Bean>: toMapBeans(beans, t -> t.getId())
     */
    public static <F, B> Map<F, B> toMapBeans(Collection<B> beans, Function<B, F> field) {
        return beans.stream().collect(Collectors.toMap(field, b -> b, (k1, k2) -> k2));
    }

    /**
     * leftBeans + rightMap -> leftBeans
     * 注：leftBeans 与 rightMap 属性名要一致，不然手动拼装
     */
    public static <B, K, V> void join(Collection<B> leftBeans, Function<B, K> leftFK, Map<K, V> rightMap, BeanCopier copier) {
        leftBeans.stream().forEach(b -> {
            if (rightMap.containsKey(leftFK.apply(b))) {
                copier.copy(rightMap.get(leftFK.apply(b)), b, null);
            }
        });
    }

    /**
     * leftBeans + rightBeans -> leftBeans
     * 注：leftBeans 与 rightMap 属性名要一致，不然手动拼装
     */
    public static <L ,R, K> void join(List<L> leftBeans, Function<L,K> leftFK, Class<L> leftClass,
                                      List<R> rightBeans, Function<R,K> rightPK, Class<R> rightClass) {
        BeanCopier copier = getCopier(rightClass, leftClass);
        join(leftBeans, leftFK, rightBeans, rightPK, copier);
    }

    /**
     * leftBeans + rightBeans -> leftBeans
     */
    public static <L ,R, K> void join(List<L> leftBeans, Function<L,K> leftFK,
                                      List<R> rightBeans, Function<R,K> rightPK,
                                      BeanCopier copier) {
        Assert.isTrue(leftBeans.size() == rightBeans.size(), "需要leftBeans.size() == rightBeans.size()");
        Map<K, R> rightMap = toMapBeans(rightBeans, rightPK);
        join(leftBeans, leftFK, rightMap, copier);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Role {
    private Long id = 1L;
    private String name = "role";
    private String rolename = "rolename";
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Rel {
    private Integer roleId = 1;
    private Long userId = 1L;
    private String name = "rel";
    private String relname = "relname";
}

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
class User {
    private Long id = 1L;
    private String name = "user";
    private String username = "username";
}

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
class Staff {
    private Long id = 2L;
    private String name="staff";
    private Long roleId;
    private Long relId;
    private Long userId = 1L;
    private String rolename;
    private String relname;
    private String username;
}

